﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Reflection.Emit;
using CharacterCreation.Business.Repositories;
using System.Collections;
using CharacterCreation.Business.ViewModels;
using CharacterCreation.Data;

namespace System.Web.Mvc
{
    public static class Helpers
    {
        private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
        {
            Type realModelType = modelMetadata.ModelType;

            Type underlyingType = Nullable.GetUnderlyingType(realModelType);
            if (underlyingType != null)
            {
                realModelType = underlyingType;
            }
            return realModelType;
        }

        private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = "", Value = "" } };

        public static string GetEnumDescription<TEnum>(TEnum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());

            DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if ((attributes != null) && (attributes.Length > 0))
                return attributes[0].Description;
            else
                return value.ToString();
        }

        public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
        {
            return EnumDropDownListFor(htmlHelper, expression, null);
        }

        public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            Type enumType = GetNonNullableModelType(metadata);
            IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();

            IEnumerable<SelectListItem> items = from value in values
                                                select new SelectListItem
                                                {
                                                    Text = GetEnumDescription(value),
                                                    Value = value.ToString(),
                                                    Selected = value.Equals(metadata.Model)
                                                };

            // If the enum is nullable, add an 'empty' item to the collection
            if (metadata.IsNullableValueType)
                items = SingleEmptyItem.Concat(items);

            return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
        }

        public static void EnumDropDownListFor<TModel>(this HtmlHelper<TModel> htmlHelper, PropertyInfo pInfo, object htmlAttributes)
        {
            ModelMetadata metadata = htmlHelper.ViewData.ModelMetadata;
            Type enumType = pInfo.PropertyType;
            var values = Enum.GetValues(enumType);

            List<SelectListItem> items = new List<SelectListItem>();
            for (int i = 0; i < values.Length; ++i)
            {
                items.Add(
                    new SelectListItem
                    {
                        Text = GetEnumDescription(values.GetValue(i)),
                        Value = values.GetValue(i).ToString(),
                        Selected = values.GetValue(i).Equals(metadata.Model)
                    });
            }

            htmlHelper.ViewData["enumValues"] = new SelectList(items);
            //return MvcHtmlString.Create("<h1>carlos</h1>");
        }

        public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression, string partialViewName)
        {
            string name = ExpressionHelper.GetExpressionText(expression);
            object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
            var viewData = new ViewDataDictionary(helper.ViewData)
            {
                TemplateInfo = new System.Web.Mvc.TemplateInfo
                {
                    HtmlFieldPrefix = name
                }
            };

            return helper.Partial(partialViewName, model, viewData);

        }

        /// <summary>
        /// Fills the property and returns the collection.
        /// </summary>
        /// <typeparam name="ViewModel"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="viewModel"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static TProperty Load<ViewModel, TProperty>(this ViewModel viewModel, System.Linq.Expressions.Expression<Func<ViewModel, TProperty>> expression)
        {
            if (typeof(TProperty).IsGenericType)
            {
                Type parameterType = typeof(TProperty).GetGenericArguments()[0];
                var repos = typeof(RepositoryFactory)
                    .GetMethod("Create")
                    .MakeGenericMethod(new Type[] { parameterType })
                    .Invoke(null, new object[] { string.Empty });

                string identityColumnName = MetaDataStore.IdentityFieldName(ModelBinder.Instance.GetModelType(typeof(ViewModel)));
                Guid id = (Guid)viewModel.GetType().GetProperty(identityColumnName).GetValue(viewModel, null);
                string where = string.Format("{0}Id = '{1}'", typeof(ViewModel).Name.Remove(0, 2), id);

                var result = (repos
                   .GetType()
                   .GetMethod("Get", new Type[] { typeof(string) })
                   .Invoke(repos, new object[] { where }));

                viewModel.GetType().GetProperty(((MemberExpression)expression.Body).Member.Name).SetValue(viewModel, (TProperty)result, null);
                return (TProperty)result;
            }

            return default(TProperty);
        }
    }
}
